/**
 * class which represent the Mathematic Expression of Polynomail - a[1]+a[2]*x+a[3]*(x^2)+...+a[N]*(x^(N-1))
 * When :
 * X is a single Variable,
 * a[1]..a[N] are real numbers, 
 * N is a final natural number  - the degree of the Polynomail
 * @author Nadav Cohen, Matan Monitz
 *
 */
public class Polynomial implements Expression{
	
	private Variable var;			// the polynomail variable
	private double[] coefficients;  // an array of real number which represent a[1]..a[N], and the index of each real number represent N.
									// the length of coeffients represent the degree of Polynomail
	
/**
 * 	Constructor Method -
 * create a new polynomail object with exp1 parameter as the left expression (A), and exp2 parameter as the right expression (B)
 * copy pointers of Expressions exp1 & exp2, doesnt create new Expressions.
 * *in case coeffecients parameter is valid but without any coeffiecents - create a polynomail only with a[1] coeffiecnt, with value 0.* 
 * @param var - object which represnt a single Variable.
 * 				valid only if truly exist - pointer points to an existing object (not null).
 * @param coeffecients - an array of real numbers.
 * 						a valid coeffecients parameter is not null (existing array)
 * 						in case coeffecients array length is zero - create 

*/	
	public Polynomial(Variable var,double coefficients[]){
		if(var==null)
			throw new RuntimeException("Polynomial couldn't iniliazed - Variable given uninitialzed!");
		if(coefficients==null)
			throw new RuntimeException("Polynomial couldn't iniliazed - double array given uninitialzed!");
		
		this.var=var;
		if(coefficients.length==0)	
			this.coefficients = new double[] {0};
		else
		{
			this.coefficients = new double[coefficients.length];
			for(int i=0;i<coefficients.length;i++)
				this.coefficients[i]=coefficients[i];  // copy values of given coeffients
		}
	}
	
	public double evaluate(Assignments assignments) {
		if(assignments==null)			// incase parameter given points to nowhere, throw exception
			throw new RuntimeException("Polynomial evalutation didnt succeed - Assignments given uninitialzed!");
		double sum=coefficients[0];		// sum = summary of the Polynomial objects values. by definition as seen in Constructor - each Polynomial will have at least a[1]
		for(int i=1;i<coefficients.length;i++)		//calculate the object in place a[i]*(x^(i)), and add it to the summary.
			sum=sum+coefficients[i]*(Math.pow(assignments.valueOf(var), i));  	
		
		return sum;  // return the summary
	}

	
	public Expression derivative(Variable var) {
		if(var==null)						// incase parameter given points to nowhere, throw exception (doesnt know by which variable to derive)
			throw new RuntimeException("parameter error, var not initialized");
		if(!this.var.equals(var))			// in case derive not by this Polynomail variable - regard all Polynomail objects as Constant - derive of constant is constant 0; 
			return new Constant(0);
		double[] coefficients = new double[this.coefficients.length-1];  // derive of Polynomail is a new Polynomail but one degree down (N-1)
		for(int i=1;i <= coefficients.length;i++)						// derive each object of Polynomail - each a[i]*(x^(i-1))
			coefficients[i-1]=this.coefficients[i]*i;
		return new Polynomial(this.var,coefficients);
			
	}

	/**
	* compare parameter Object other to this Polynomail. Objects Equals if:
	* - parameter other is type Polynomail and not null
	* - parameter other is the same degree as this Polynomail 
	* - parameter other variable is the same as this Polynomail variable.
	* - the coeffiecnts of other are indintical to this Polynomail coeffiecnts  
	*  @return true if equals, else return false.
	*/
	public boolean equals(Object other){
		boolean ans=false;
		if(other instanceof Polynomial)
		{
			if(coefficients.length!=((Polynomial)other).coefficients.length) // coefficients length = the degree of the Polynomail
				return false;
			ans = (((Polynomial)other).var.equals(this.var));				// check that variables indentical
			for(int i=0;ans && i<coefficients.length;i++)					// check that all coeffients are identical
				ans = (coefficients[i]==((Polynomial)other).coefficients[i]);
		}	
		return ans;	
	}
	
	public String toString(){
		String str="(";
		for(int i=0;i<coefficients.length;i++)
		{
			if(i==0)			//add a[1] to string - without variable 
				str=str+coefficients[i];
			else
			{
				if(coefficients[i]>=0)	// incase positive number - add + to represent addition. else - printed negitive number "-" as defult in java
					str=str + "+";
				str=str + coefficients[i] + "*" + var.toString();
				if(i>1)					//a[2] is with variable but with exponent 1 -exponent 1 doesnt need to be printed
					str=str + "^" + i;
			}	
		}
		return str + ")";
	}
}